package com.chen.blue.security.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.chen.blue.domain.model.system.LoginUser;
import com.chen.blue.domain.model.system.UsersModel;
import com.chen.blue.domain.vo.system.DeptVo;
import com.chen.blue.exception.e.DtoNullException;
import com.chen.blue.exception.e.TokenException;
import com.chen.blue.redis.service.RedisService;
import com.chen.blue.result.DataResult;
import com.chen.blue.result.R;
import com.chen.blue.service.security.SecurityService;
import com.chen.blue.service.system.*;
import com.chen.blue.sysconfig.SystemConfig;
import com.chen.blue.utils.ServletUtil;
import com.chen.blue.utils.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Sets;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created By CLT On 2024/4/26.
 */
@SuppressWarnings("all")
@Slf4j
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {

    private SystemConfig systemConfig;

    private RedisService redisService;

    private IUsersService userService;

    private IDeptService iDeptService;

    private IDataScopeUserDeptService iDataScopeUserDeptService;

    private IUserRoleService iUserRoleService;

    private IMenuRoleService iMenuRoleService;

    public TokenAuthenticationFilter(AuthenticationManager authenticationManager, RedisService redisService, IUsersService userService, IDeptService iDeptService, IDataScopeUserDeptService iDataScopeUserDeptService, IUserRoleService iUserRoleService, IMenuRoleService iMenuRoleService, SystemConfig systemConfig) {

        super(authenticationManager);
        this.redisService = redisService;
        this.userService = userService;
        this.iDeptService = iDeptService;
        this.iDataScopeUserDeptService = iDataScopeUserDeptService;
        this.iUserRoleService = iUserRoleService;
        this.iMenuRoleService = iMenuRoleService;
        this.systemConfig = systemConfig;

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        try {
            String authToken = request.getHeader(systemConfig.getTokenHeader());
            if (Objects.nonNull(authToken) && StrUtil.isNotBlank(authToken)) {

                String userName = null;

                userName = TokenUtil.getUserName(authToken, systemConfig.getTokenSign());

                validToken(userName, authToken);

                String[] roleArray = roleArray(authToken, userName);

                Collection<GrantedAuthority> grantedAuthorityCollection = null;
                if (Objects.nonNull(roleArray)) {
                    grantedAuthorityCollection = new ArrayList<>();
                    for (String role : roleArray) {
                        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role);
                        grantedAuthorityCollection.add(simpleGrantedAuthority);
                    }
                }

                SecurityService.setAuthentication(buildData(userName, roleArray, response), grantedAuthorityCollection, request);


            }
        } catch (TokenException e) {
            ServletUtil.renderString(JSONUtil.toJsonStr(R.success(e.getMessage())), response);
            return;
        } catch (Exception e) {
            e.printStackTrace();
        }

        chain.doFilter(request, response);

    }


    private void validToken(String username, String token) throws TokenException {

        if (Objects.isNull(username) || Objects.isNull(token))
            return;

        String key = systemConfig.getTokenSave() + username;

        boolean isExist = redisService.isExist(key);

        if (isExist) {
            String currentToken = redisService.get(key);
            if (!(token.equals(currentToken))) {
                throw new TokenException("该账号已在其他地方登录");
            }
        } else {
            throw new TokenException("登录失效，请先登录");
        }


    }

    /**
     * 添加权限
     *
     * @return
     */
    private String[] roleArray(String authToken, String username) {

        if (Objects.isNull(authToken) || Objects.isNull(username))
            return null;

        String roleStr = redisService.get(systemConfig.getTokenSign() + username);
        if (Objects.nonNull(roleStr) && StrUtil.isNotBlank(roleStr)) {

            return roleStr.split(",");

        }

        return null;

    }


    /**
     * 构建当前用户信息
     *
     * @param username
     * @return
     */
    private LoginUser buildLoginUser(String username) {

        if (Objects.isNull(username))
            return null;

        UsersModel userModel = userService.getUserByUsername(username);
        LoginUser loginUser = new LoginUser();
        loginUser.setSysUser(userModel);
        loginUser.setUsername(username);
        return loginUser;

    }

    /**
     * 构建数据权限所需要的数据
     *
     * @param username
     * @return
     */
    private Map<String, Object> buildData(String username, String[] roleArr, HttpServletResponse response) throws DtoNullException {

        if (Objects.isNull(username) || Objects.isNull(roleArr))
            return null;

        Map<String, Object> data = new HashMap<>();
        LoginUser loginUser = buildLoginUser(username);
        UsersModel sysUser = loginUser.getSysUser();
        data.put("loginInfo", loginUser);
        // todo 将数据权限所需的数据提前准备好

        buildDeptChildrenIds(sysUser, data);
        buildSelectedDeptIds(sysUser, data);
        buildUserMenuIds(sysUser, data, response);
        buildUserNameByDeptId(data, sysUser);
        buildUserNameByDeptId(sysUser, data);
        data.put("userRoles", Arrays.asList(roleArr));

        return data;

    }


    /**
     * 当前用户所拥有的菜单并集
     *
     * @param sysUser
     * @param data
     * @throws DtoNullException
     */
    protected void buildUserMenuIds(UsersModel sysUser, Map<String, Object> data, HttpServletResponse response) throws DtoNullException {

        if (Objects.isNull(sysUser) || Objects.isNull(data))
            return;

        DataResult<List<Long>> var1 = iUserRoleService.queryRoleIdById(sysUser.getId());
        List<Long> roleIds = var1.getResult();

        if (Objects.isNull(roleIds)) {
            return;
        }

        DataResult<List<Long>> var2 = iMenuRoleService.queryMenuIdsByIds(roleIds);
        List<Long> menuIds = var2.getResult();

        Set<Long> menuIdsNotRepeat = new HashSet<>();
        for (Long menuId : menuIds) {
            menuIdsNotRepeat.add(menuId);
        }

        data.put("menuIds", menuIdsNotRepeat);


    }

    /**
     * 构建当前用户的自定义部门
     *
     * @param sysUser
     * @param data
     * @throws DtoNullException
     */
    protected void buildSelectedDeptIds(UsersModel sysUser, Map<String, Object> data) throws DtoNullException {

        if (Objects.isNull(sysUser) || Objects.isNull(data))
            return;

        DataResult<Map<String, Object>> dataResult = iDataScopeUserDeptService.queryDataScopeUserDeptVoById(sysUser.getId());
        Map<String, Object> result = (Map<String, Object>) dataResult.getResult();

        data.put("selectedDeptIds", result.get("checkedKeys"));

    }

    /**
     * 构建当前用户相关的部门及子部门
     *
     * @param sysUser
     * @param data
     * @return
     * @throws DtoNullException
     */
    protected void buildDeptChildrenIds(UsersModel sysUser, Map<String, Object> data) throws DtoNullException {

        if (Objects.isNull(sysUser) || Objects.isNull(data))
            return;

        DataResult<List<DeptVo>> deptVoDataResult = iDeptService.queryDeptById(sysUser.getDeptId());
        List<DeptVo> list = deptVoDataResult.getResult();
        Set<Long> deptIds = new HashSet<>();

        getId(list, deptIds);

        data.put("deptChildrenIds", deptIds);

    }

    private void getId(List<DeptVo> list, Set<Long> deptIds) {

        if (Objects.isNull(list) || list.isEmpty())
            return;

        for (DeptVo deptVo : list) {
            deptIds.add(deptVo.getId());
            if ((deptVo.getChildren() != null) || !deptVo.getChildren().isEmpty()) {
                getId(deptVo.getChildren(), deptIds);
            }
        }

    }

    protected void buildUserNameByDeptId(Map<String, Object> data, UsersModel model) {

        if (Objects.isNull(data) || Objects.isNull(model))
            return;

        Object deptChildrenIds = data.get("deptChildrenIds");
        if (Objects.isNull(deptChildrenIds) || Objects.isNull(data) || Objects.isNull(model))
            return;

        Set<Long> deptIds = (Set<Long>) deptChildrenIds;
        List<String> userNames = userService.getUsersByDeptId(deptIds).stream().map(obj -> {
            return obj.getUsername();
        }).collect(Collectors.toList());
        data.put("creators", userNames);

        Object selectedDeptIds = data.get("selectedDeptIds");
        if (Objects.isNull(selectedDeptIds))
            return;

        Set<Long> var1 = (Set<Long>) selectedDeptIds;

        List<String> selectUserNames = userService.getUsersByDeptId(var1).stream().map(obj -> {
            return obj.getUsername();
        }).collect(Collectors.toList());
        data.put("creatorsSelect", selectUserNames);

        Set<Long> ids = new HashSet<>();
        ids.add(model.getDeptId());
        List<String> currentDeptUserNames = userService.getUsersByDeptId(ids).stream().map(obj -> {
            return obj.getUsername();
        }).collect(Collectors.toList());
        data.put("creatorsCurrent", currentDeptUserNames);


    }

    /**
     * 竞赛活动表相关
     */
    protected void buildUserNameByDeptId(UsersModel sysUser, Map<String, Object> data) {

        if (Objects.isNull(sysUser)) {
            return;
        }

        // 本部门数据权限
        List<UsersModel> usersModelListVar1 = userService.getUsersByDeptId(Sets.newHashSet(sysUser.getDeptId()));
        if (Objects.nonNull(usersModelListVar1) && !usersModelListVar1.isEmpty()) {

            List<String> userNames = usersModelListVar1.stream().map(obj -> {
                return obj.getUsername();
            }).collect(Collectors.toList());
            data.put("creators_active_own_dept", userNames);

        }

        // 本部门及部门以下数据权限
        Set<Long> usersModelListVar2 = (Set<Long>) data.get("deptChildrenIds");
        if (Objects.nonNull(usersModelListVar2) && !usersModelListVar2.isEmpty()) {
            List<UsersModel> usersByDeptId = userService.getUsersByDeptId(usersModelListVar2);
            if (Objects.nonNull(usersByDeptId) && !usersByDeptId.isEmpty()) {

                List<String> userNames = usersByDeptId.stream().map(obj -> {
                    return obj.getUsername();
                }).collect(Collectors.toList());
                data.put("creators_active_child_dept", userNames);

            }
        }

        // 所选部门数据权限
        Set<Long> selectedDeptIds = (Set<Long>) data.get("selectedDeptIds");
        if (Objects.nonNull(selectedDeptIds) && !selectedDeptIds.isEmpty()) {
            List<UsersModel> usersModelListVar3 = userService.getUsersByDeptId(selectedDeptIds);
            if (Objects.nonNull(usersModelListVar3) && !usersModelListVar3.isEmpty()) {

                List<String> userNames = usersModelListVar3.stream().map(obj -> {
                    return obj.getUsername();
                }).collect(Collectors.toList());
                data.put("creators_active_select_dept", userNames);

            }
        }

    }


}
